﻿//-----------------------------------------------------------------------
// <copyright file="CssBuilder.cs" company="Erik Lieben">
//     Copyright Erik Lieben. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace ErikLieben.CssPreprocessor
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text.RegularExpressions;

    /// <summary>
    /// Works for now, class to build a definition of the css code
    /// Just get everything working then re-factor to get nicer code
    /// </summary>
    public static class CssBuilder
    {
        /// <summary>
        /// Builds the CSS definition.
        /// </summary>
        /// <param name="css">The CSS to build a list of definitions from.</param>
        /// <returns>a list of objects containing the css definition as defined in the css string given</returns>
        internal static List<CssDefinition> BuildCssDefinition(string css)
        {
            List<CssDefinition> cssContainer = new List<CssDefinition>();
            foreach (Match match in Regex.Matches(css, "(?<selector>.|[^}]*?)\\{(?<inner>.*?)\\}"))
            {
                CssDefinition cssDefinition = new CssDefinition();
                foreach (string selector in match.Groups["selector"].Value.Split(new[] { ',' }))
                {
                    Match matchSelector = Regex.Match(selector, "(?<selector>.[^:|\\(]*)((:(?<pseudo>.*))|(\\((?<params>.*)\\)))?");

                    string sel = matchSelector.Groups["selector"].Success ? matchSelector.Groups["selector"].Value : null;
                    string psuedo = matchSelector.Groups["pseudo"].Success ? matchSelector.Groups["pseudo"].Value : null;
                    string param = matchSelector.Groups["params"].Success ? matchSelector.Groups["params"].Value : null;

                    if (!String.IsNullOrWhiteSpace(sel) && !String.IsNullOrWhiteSpace(psuedo))
                    {
                        cssDefinition.Selectors.Add(new CssSelector { Name = sel.Trim(), Pseudo = psuedo.Trim() });
                    }
                    else if (!String.IsNullOrWhiteSpace(sel) && !String.IsNullOrWhiteSpace(param))
                    {
                        var newsel = new CssSelector { Name = sel.Trim() };
                        foreach (string val in param.Split(new[] { ',' }))
                        {
                            var parts = val.Trim().Split(new[] { ':' });
                            newsel.Parameters.Add(parts[0].Trim(), parts[1].Trim());
                        }

                        cssDefinition.Selectors.Add(newsel);
                    }
                    else if (!String.IsNullOrWhiteSpace(sel))
                    {
                        cssDefinition.Selectors.Add(new CssSelector { Name = sel.Trim() });
                    }
                }

                foreach (string cssdef in match.Groups["inner"].Value.Split(new[] { ';' }))
                {
                    string[] parts = cssdef.Split(new[] { ':' });
                    if (parts.Length != 2)
                    {
                        continue;
                    }

                    cssDefinition.Properties.Add(new CssProperty { Name = parts[0].Trim(), Value = parts[1].Trim() });
                }

                cssContainer.Add(cssDefinition);
            }

            return cssContainer;
        }

        /// <summary>
        /// Processes the CSS extensions in the definition.
        /// </summary>
        /// <param name="cssContainer">The CSS container to process.</param>
        /// <returns>The css container with the extensions applied (and removed)</returns>
        internal static List<CssDefinition> ProcessCssExtensions(List<CssDefinition> cssContainer) 
        {
            // TODO: use where instead of foreach, etc.
            foreach (CssDefinition cssDef in cssContainer)
            {
                List<CssProperty> removeItems = new List<CssProperty>();
                List<CssProperty> addItems = new List<CssProperty>();
                foreach (CssProperty item in cssDef.Properties)
                {
                    if (item.Name.StartsWith("-extend"))
                    {
                        Regex expression = new Regex("(?<extension>.*)(\\((?<params>.*)\\))?", RegexOptions.None);
                        Match match = expression.Match(item.Value);
                        string extName = match.Groups["extension"].Value;
                        string extParams = match.Groups["params"].Value;
                        removeItems.Add(item);

                        if (String.IsNullOrWhiteSpace(extName))
                        {
                            continue;
                        }

                        var foundItem = cssContainer.Where(c => c.Selectors.Any(i => i.Name.StartsWith("." + extName))).FirstOrDefault();
                        if (foundItem != null)
                        {
                            foreach (var p in foundItem.Properties)
                            {
                                var replace = p.Value;

                                var parameters = extParams.Split(new[] { ',' });
                                for (int i = 0; i < parameters.Length; i++)
                                {
                                    if (String.IsNullOrWhiteSpace(parameters[i]))
                                    {
                                        parameters[i] = foundItem.Selectors[0].Parameters.ElementAt(0).Value;
                                    }

                                    replace = replace.Replace(foundItem.Selectors[0].Parameters.ElementAt(i).Key, parameters[i]);
                                }

                                addItems.Add(new CssProperty { Name = p.Name, Value = replace });
                            }
                        }
                    }
                }

                foreach (var item in removeItems)
                {
                    cssDef.Properties.Remove(item);
                }

                foreach (var item in addItems)
                {
                    cssDef.Properties.Add(item);
                }
            }

            return cssContainer;
        }

        /// <summary>
        /// Builds the CSS code from a list of css definitions.
        /// </summary>
        /// <param name="cssContainer">The CSS container to build the css code from.</param>
        /// <returns>the css code as defined in the given list</returns>
        internal static string BuildCssCode(List<CssDefinition> cssContainer)
        {
            // Output new css
            string cssCode = string.Empty;
            foreach (var cssDef in cssContainer)
            {
                for (int i = 0; i < cssDef.Selectors.Count; i++)
                {
                    cssCode += cssDef.Selectors[i].Name;
                    if (!String.IsNullOrWhiteSpace(cssDef.Selectors[i].Pseudo))
                    {
                        cssCode += ":" + cssDef.Selectors[i].Pseudo;
                    }

                    if (i < cssDef.Selectors.Count - 1)
                    {
                        cssCode += ",";
                    }
                }

                cssCode += "{";
                for (int i = 0; i < cssDef.Properties.Count; i++)
                {
                    cssCode += string.Format("{0}:{1}", cssDef.Properties[i].Name, cssDef.Properties[i].Value);
                    if (i < cssDef.Properties.Count - 1)
                    {
                        cssCode += ";";
                    }
                }

                cssCode += "}";
            }

            return cssCode;
        }
    }
}
